package com.clei.utils;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.StringWriter;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.function.Function;

/**
 * 流处理工具
 *
 * @author Y
 * @date 2022-05-13
 */
public class StreamUtil {

    /**
     * 默认的buffer大小
     */
    private static final int BUFFER_SIZE = 1024;

    /**
     * 默认编码
     */
    private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;

    /**
     * stream -> String
     *
     * @param is InputStream
     */
    public static String toString(InputStream is) {
        return toString(is, DEFAULT_CHARSET);
    }

    /**
     * stream -> String
     *
     * @param is      InputStream
     * @param charset charset
     */
    public static String toString(InputStream is, Charset charset) {
        String res = null;
        int bufferSize = 32;
        char[] buf = new char[bufferSize];
        try (InputStreamReader isr = new InputStreamReader(is, charset);
             StringWriter sw = new StringWriter()) {
            int length = -1;
            while (-1 != (length = isr.read(buf))) {
                sw.write(buf, 0, length);
            }
            sw.flush();
            res = sw.toString();
        } catch (Exception e) {
            PrintUtil.log("toString error ", e);
        }
        return res;
    }

    /**
     * stream -> byte
     *
     * @param is InputStream
     * @return byte[]
     */
    public static byte[] toBytes(InputStream is) {
        try (InputStream input = is) {
            byte[] res = new byte[input.available()];
            byte[] buffer = new byte[BUFFER_SIZE];
            int length;
            int targetPos = 0;
            while (-1 != (length = input.read(buffer))) {
                System.arraycopy(buffer, 0, res, targetPos, length);
                targetPos += length;
            }
            return res;
        } catch (Exception e) {
            PrintUtil.log("toBytes 出错", e);
            return null;
        }
    }

    /**
     * stream -> hex
     *
     * @param is InputStream
     * @return hex
     */
    public static String toHex(InputStream is) {
        return toHex(is, false);
    }

    /**
     * stream -> hex
     *
     * @param is        InputStream
     * @param upperCase 是否大写 true/false
     * @return hex
     */
    public static String toHex(InputStream is, boolean upperCase) {
        try (InputStream input = is) {
            StringBuilder sb = new StringBuilder(input.available() * 2);
            byte[] buffer = new byte[BUFFER_SIZE];
            int length;
            while (-1 != (length = input.read(buffer))) {
                byte[] bytes = Arrays.copyOfRange(buffer, 0, length);
                String hex = ByteUtil.toHex(bytes, upperCase);
                sb.append(hex);
            }
            return sb.toString();
        } catch (Exception e) {
            PrintUtil.log("toHex 出错", e);
            return null;
        }
    }

    /**
     * 转移
     * 将inputStream里的数据转移到outputStream
     *
     * @param inputStream  输入流
     * @param outputStream 输出流
     */
    public static void transfer(InputStream inputStream, OutputStream outputStream) {
        bytesConvert(inputStream, outputStream, bytes -> bytes);
    }

    /**
     * 二进制数据转换
     *
     * @param inputStream  输入流
     * @param outputStream 输出流
     * @param converter    转换器
     */
    public static void bytesConvert(InputStream inputStream, OutputStream outputStream, Function<byte[], byte[]> converter) {
        try (InputStream is = inputStream;
             OutputStream os = outputStream) {
            int bufferSize = 1024;
            byte[] buffer = new byte[bufferSize];
            int length = -1;
            while (-1 != (length = is.read(buffer))) {
                os.write(converter.apply(buffer), 0, length);
            }
            os.flush();
        } catch (Exception e) {
            PrintUtil.log("数据转换出错", e);
        }
    }

    /**
     * 转移
     * 将inputStream里的数据转移到outputStream
     *
     * @param inputStream  输入流
     * @param outputStream 输出流
     */
    public static void nioTransfer(InputStream inputStream, OutputStream outputStream) {
        nioTransfer(inputStream, outputStream, true);
    }

    /**
     * 转移
     * 将inputStream里的数据转移到outputStream
     *
     * @param inputStream  输入流
     * @param outputStream 输出流
     */
    public static void nioTransfer(InputStream inputStream, OutputStream outputStream, boolean autoClose) {
        ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
        if (autoClose) {
            try (ReadableByteChannel readChannel = Channels.newChannel(inputStream);
                 WritableByteChannel writeChannel = Channels.newChannel(outputStream)) {
                while (readChannel.read(buffer) != -1) {
                    //切换为读状态
                    buffer.flip();
                    //保证全部写入
                    while (buffer.hasRemaining()) {
                        writeChannel.write(buffer);
                    }
                    //清空缓冲区
                    buffer.clear();
                }
            } catch (Exception e) {
                PrintUtil.log("nioTransfer error ", e);
            }
        } else {
            try {
                ReadableByteChannel readChannel = Channels.newChannel(inputStream);
                WritableByteChannel writeChannel = Channels.newChannel(outputStream);
                while (readChannel.read(buffer) != -1) {
                    //切换为读状态
                    buffer.flip();
                    //保证全部写入
                    while (buffer.hasRemaining()) {
                        writeChannel.write(buffer);
                    }
                    //清空缓冲区
                    buffer.clear();
                }
            } catch (Exception e) {
                PrintUtil.log("nioTransfer error ", e);
            }
        }
    }
}